package com.smart.uart;


import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * Created by CVTE on 2015/12/25.
 */
public class SerialDataUtil {
    private static final String TAG = "SerialDataUtil";
    public static final String DATA = "SerialData";
    /*control code*/
    public static final byte E_SEND_ERROR_CONTROL = 0x00;
    public static final byte E_SEND_USB_SWITCH_CONTROL = (byte) 0x01;
    public static final byte E_SEND_GPIO_STATUS_CONTROL = (byte) 0x02;
    public static final byte E_SEND_SOFTWARE_VERSION_CONTROL = (byte) 0x03;
    public static final byte E_SEND_MCU_ALIVE_CONTROL = (byte) 0x04;
    public static final byte E_SEND_TEST_UART_CONTROL = (byte) 0x05;
    public static final byte E_SEND_TEST_NET_PORT_CONTROL = (byte) 0x06;

    public static final byte E_RECE_ERROR_CONTROL = 0x00;
    public static final byte E_RECE_USB_SWITCH_CONTROL = (byte) 0x01;
    public static final byte E_RECE_GPIO_STATUS_CONTROL = (byte) 0x02;
    public static final byte E_RECE_SOFTWARE_VERSION_CONTROL = (byte) 0x03;
    public static final byte E_RECE_MCU_ALIVE_CONTROL = (byte) 0x04;
    public static final byte E_RECE_TEST_UART_CONTROL = (byte) 0x05;
    public static final byte E_RECE_TEST_NET_PORT_CONTROL = (byte) 0x06;
    public static final byte E_RECE_UNKONW_CONTROL = (byte) 0xDF;

    //update mcu
    public static final byte UPDATE_MCU_ACK = (byte)0x79;
    public static final byte UPDATE_MCU_NACK = (byte)0x1F;
    //from 0xE0
    public static final byte MSG_MCU_UPDATE_NO_ACTION = (byte)0xE0;
    public static final byte MSG_MCU_UPDATE_START = (byte)0xE1;
    public static final byte MSG_MCU_UPDATE_GPIO_INIT = (byte)0xE2;
    public static final byte MSG_MCU_UPDATE_USART_INIT = (byte)0xE3;
    public static final byte MSG_MCU_UPDATE_SEND_ERASE_COMMAND = (byte)0xE4;
    public static final byte MSG_MCU_UPDATE_MASS_ERASE = (byte)0xE5;
    public static final byte MSG_MCU_UPDATE_WRITE_BIN_COMMAND = (byte)0xE6;
    public static final byte MSG_MCU_UPDATE_WRITE_BIN_ADDRESS = (byte)0xE7;
    public static final byte MSG_MCU_UPDATE_WRITE_BIN_DATA = (byte)0xE8;
    public static final byte MSG_MCU_UPDATE_GPIO_RECOVER = (byte)0xE9;
    public static final byte MSG_MCU_UPDATE_END = (byte)0xEA;

    public static final byte STATUS_MCU_UPDATE_NO_ACTION = (byte)0xF0;
    public static final byte STATUS_MCU_UPDATE_START = (byte)0xF1;
    public static final byte STATUS_MCU_UPDATE_GPIO_INIT = (byte)0xF2;
    public static final byte STATUS_MCU_UPDATE_USART_INIT = (byte)0xF3;
    public static final byte STATUS_MCU_UPDATE_SEND_ERASE_COMMAND = (byte)0xF4;
    public static final byte STATUS_MCU_UPDATE_MASS_ERASE = (byte)0xF5;
    public static final byte STATUS_MCU_UPDATE_WRITE_BIN_COMMAND = (byte)0xF6;
    public static final byte STATUS_MCU_UPDATE_WRITE_BIN_ADDRESS = (byte)0xF7;
    public static final byte STATUS_MCU_UPDATE_WRITE_BIN_DATA = (byte)0xF8;
    public static final byte STATUS_MCU_UPDATE_GPIO_RECOVER = (byte)0xF9;
    public static final byte STATUS_MCU_UPDATE_END = (byte)0xFA;

    public static final byte MCU_UPDATE_STATUS_CONTROL = (byte)0xFF;
    public static final byte MCU_UPDATE_STATUS_FAIL = (byte)0x00;
    public static final byte MCU_UPDATE_STATUS_SUCCESS = (byte)0x01;
    public static final byte MCU_UPDATE_STATUS_NOT_FILE = (byte)0x02;
    public static final byte MCU_UPDATE_STATUS_UPDATING = (byte)0x03;
    public static final byte MCU_UPDATE_STATUS_UPDATE_START = (byte)0x04;

    private static final String MCU_UPDATE_FILE_NAME = "SmartMcuTestFW.bin";
    private static final String MCU_UPDATE_FILE_PATH = "/data/firmware";
    private static final String STORAGE_EXTERNAL = "/mnt/usb/";
    private static byte mcuUpdateStatus = STATUS_MCU_UPDATE_NO_ACTION;
    private static final int FLASH_SIZE = (1024*1024);
    private static final int MAX_WRITE_DATA_SIZE = 256;
    private static List<String> getMountPathList() {
        List<String> filepaths = new ArrayList<>();

        // scan the usb or ext_sdcard , they all mount on /mnt/usb
        String[] scanList = {STORAGE_EXTERNAL,};
        for (String dir : scanList) {
            File rootFile = new File(dir);
            if (rootFile != null && rootFile.exists()) {
                for (File file : rootFile.listFiles()) {
                    if (file.isDirectory() && (file.canRead() || file.canWrite())) {
                        filepaths.add(file.getPath());
                    }
                }
            }
        }
        return filepaths;
    }
    public static McuBinData getMcuBinData(){
        McuBinData mcuBinData = new McuBinData();
        List<String> filePaths = getMountPathList();
        String strBinFile = null;
        for(int i = 0;i < filePaths.size();i++){
            if((new File(filePaths.get(i) + '/' + MCU_UPDATE_FILE_NAME).exists())){
                strBinFile = filePaths.get(i) + '/' + MCU_UPDATE_FILE_NAME;
                break;
            }
        }
        if(strBinFile == null) {
            if ((new File(MCU_UPDATE_FILE_PATH + '/' + MCU_UPDATE_FILE_NAME).exists())) {
                strBinFile = MCU_UPDATE_FILE_PATH + '/' + MCU_UPDATE_FILE_NAME;
            }
        }
        if(strBinFile == null)
            return null;
        File mcuBinFile = new File(strBinFile);
        FileInputStream fisMcuFW = null;
        byte[] arrFileBuf = new byte[(int)mcuBinFile.length()];
        int fileSize = 0;
        int count = 0;
        int address;
        byte byteXORCSum;
        try {
            fisMcuFW = new FileInputStream(mcuBinFile);
            fileSize = fisMcuFW.read(arrFileBuf);
            fisMcuFW.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            if(fisMcuFW != null) {
                try {
                    fisMcuFW.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        }
        for(int i = 0; i < fileSize; i += MAX_WRITE_DATA_SIZE) {
            address = 0x08000000 + i;
            byteXORCSum = 0;
            byte[] tmpAddress = new byte[5];
            int tmpInt;
            tmpInt = ((address & 0xFF000000) >> 24);
            byteXORCSum = (byte)(byteXORCSum ^ (byte)tmpInt);
            tmpAddress[0] = ((byte)tmpInt);

            tmpInt = ((address & 0x00FF0000) >> 16);
            byteXORCSum = (byte)(byteXORCSum ^ (byte)tmpInt);
            tmpAddress[1] = ((byte)tmpInt);

            tmpInt = ((address & 0x0000FF00) >> 8);
            byteXORCSum = (byte)(byteXORCSum ^ (byte)tmpInt);
            tmpAddress[2] = ((byte)tmpInt);

            tmpInt = ((address & 0x000000FF) >> 0);
            byteXORCSum = (byte)(byteXORCSum ^ (byte)tmpInt);
            tmpAddress[3] = ((byte)tmpInt);

            tmpAddress[4] = ((byte)byteXORCSum);

            mcuBinData.putAddressCom(count,tmpAddress);

            byteXORCSum = 0;
            int n = Math.min((MAX_WRITE_DATA_SIZE - 1), (fileSize - i - 1));
            byte[] tmpData = new byte[n + 3];
            byteXORCSum = (byte)(byteXORCSum ^ (byte)n);
            tmpData[0] = (byte)(n & 0xFF);

            for(int j = 0; j <= n; j++)
            {
                byteXORCSum = (byte)(byteXORCSum ^ arrFileBuf[i+j]);
                tmpData[j + 1] = arrFileBuf[i+j];
            }
            tmpData[n + 2] = byteXORCSum;
            mcuBinData.putDataBlock(count,tmpData);
            count ++;
        }

        return mcuBinData;
    }
    public static class McuBinData{
        private Map<Integer,byte[]> addressMap = null;
        private Map<Integer,byte[]> dataMap = null;
        private int mapAddressIndex;
        private int mapDataIndex;
        McuBinData(){
            addressMap = new HashMap<Integer,byte[]>();
            dataMap = new HashMap<Integer,byte[]>();
            mapAddressIndex = 0;
            mapDataIndex = 0;
        }
        public void putAddressCom(int i,byte[] bytes) {
            addressMap.put(i,bytes);
        }
        public byte[] getAddressCom() {
            if(mapAddressIndex < addressMap.size()) {
                byte[] bytes = addressMap.get(mapAddressIndex);
                mapAddressIndex++;
                return bytes;
            }else{
                return null;
            }
        }
        public void putDataBlock(int i,byte[] bytes) {
            dataMap.put(i, bytes);
        }
        public byte[] getDataBlock() {
            if(mapDataIndex < dataMap.size()) {
                byte[] bytes = dataMap.get(mapDataIndex);
                mapDataIndex++;
                return bytes;
            }else{
                return null;
            }
        }
        public boolean isDataEnd(){
            //Log.d("wuguitong","mapDataIndex = " + mapDataIndex + "   mapAddressIndex = " + mapAddressIndex);
            //Log.d("wuguitong","mapDataIndex size = " + dataMap.size() + "   mapAddressIndex size= " + addressMap.size());
            return (mapDataIndex == dataMap.size());
        }
    }
    public static byte getMcuUpdateStatus() {
        return mcuUpdateStatus;
    }

    public static void setMcuUpdateStatus(byte mcuUpdateStatus) {
        SerialDataUtil.mcuUpdateStatus = mcuUpdateStatus;
    }
    public static boolean isMcuUpdating() {
        return (mcuUpdateStatus != STATUS_MCU_UPDATE_NO_ACTION);
    }
    public static void setMcuOutOfUpdating() {
        mcuUpdateStatus = STATUS_MCU_UPDATE_NO_ACTION;
    }

    /*heard code*/
    private static final byte E_HEAD_CODE_1 = (byte) 0xAA;
    private static final byte E_HEAD_CODE_2 = (byte) 0xBB;
    private static final byte E_HEAD_CODE_3 = (byte) 0xCC;


    /*key code*/
    public static byte SWITCH_CN8_HDMI_USB = 0x00;
    public static byte SWITCH_MS828_USB = 0x01;
    //public static byte SWITCH_CN6_PC_USB = 0x02;//delete
    public static byte SWITCH_CN7_PC_USB = 0x03;
    public static byte SWITCH_J9_DP_USB = 0x04;
    public static byte SWITCH_J7_HDMI_USB = 0x08;
    public static byte SWITCH_J5_VGA_USB = 0x0c;


    /*接收数据使用
* */
    private static byte[] receBuff;
    private static int buffCurrentSize;
    private static byte currentReceControl;
    public static Queue<ReceData> byteArray;

    public static void initSerialDataUtil() {
        buffCurrentSize = 0;
        currentReceControl = E_RECE_UNKONW_CONTROL;
        receBuff = new byte[1024];
        byteArray = new ArrayBlockingQueue<ReceData>(10);
    }

    public static class ReceData {
        private byte control = E_RECE_UNKONW_CONTROL;
        private byte[] data = null;

        public void setControlDate(byte control) {
            this.control = control;
        }

        public void setReceData(byte[] bytes) {
            data = new byte[bytes.length];
            System.arraycopy(bytes, 0, data, 0, bytes.length);
        }

        public byte getControlData() {
            return this.control;
        }

        public byte[] getReceData() {
            return data;
        }
    }

    public static int decodeReceBuff(byte[] buff, int size) {
        int dsize = 0;
        if (addReceBuff(buff, size) == true) {
            ReceData data = decodeData();
            while (data != null) {
                byteArray.offer(data);
                dsize++;
                data = decodeData();
            }
            return dsize;
        } else {
            return 0;
        }
    }

    private static ReceData decodeData() {
        ReceData data = null;
        boolean isDecodeSuccess = false;
        byte length = 0;
        int startIndex = 0;
        int endIndex = 0;
        for (int i = 0; i < buffCurrentSize; i++) {
            if (receBuff[i] == E_HEAD_CODE_1) {
                startIndex = i;
                if ((buffCurrentSize > i + 5) && (buffCurrentSize >= receBuff[startIndex + 3] + i) && (receBuff[startIndex + 3] <= 64) && (receBuff[startIndex + 3] >= 0)) {
                    if (receBuff[startIndex + 1] == E_HEAD_CODE_2 && receBuff[startIndex + 2] == E_HEAD_CODE_3) {
                        length = receBuff[startIndex + 3];
                        byte datalength = (byte) (length - 6);
                        byte[] databytes = new byte[datalength];
                        System.arraycopy(receBuff, startIndex + 5, databytes, 0, datalength);
                        if (receBuff[startIndex + length - 1] == getCheckSum(receBuff[startIndex + 4], databytes, (byte) datalength)) {
                            isDecodeSuccess = true;
                            endIndex = startIndex + length;
                        }
                    }
                }
            }
            if (isDecodeSuccess) {
                byte[] recebytes = new byte[length - 6];
                data = new ReceData();
                System.arraycopy(receBuff, startIndex + 5, recebytes, 0, length - 6);
                data.setControlDate(receBuff[startIndex + 4]);
                data.setReceData(recebytes);
                buffCurrentSize -= endIndex;
                byte[] tembytes = new byte[buffCurrentSize];
                System.arraycopy(receBuff, endIndex, tembytes, 0, buffCurrentSize);
                System.arraycopy(tembytes, 0, receBuff, 0, buffCurrentSize);
                return data;
            }
        }
        return data;
    }

    public static boolean addReceBuff(byte[] buff, int size) {
        if (buffCurrentSize + size < 1024) {
            System.arraycopy(buff, 0, receBuff, buffCurrentSize, size);
            buffCurrentSize += size;
            return true;
        } else {
            buffCurrentSize = 0;
            return false;
        }
    }

    public static ReceData getCurrentReceData() {
        ReceData data = byteArray.poll();
        return data;
    }

    public static byte[] getSendUsbSwitchBuf(byte data) {
        byte[] datas = new byte[1];
        datas[0] = data;
        return getSendConformityData(E_SEND_USB_SWITCH_CONTROL, datas, (byte) 1);
    }
    public static byte[] getTestMcuUartBuf() {
        return getSendConformityData(E_SEND_TEST_UART_CONTROL, null, (byte) 0);
    }
    public static byte[] getSendGpioStatusBuf(byte data) {
        byte[] datas = new byte[1];
        datas[0] = data;
        return getSendConformityData(E_SEND_GPIO_STATUS_CONTROL, datas, (byte) 1);
    }
    public static byte[] getSendNetPortStatusBuf(byte data) {
        byte[] datas = new byte[1];
        datas[0] = data;
        return getSendConformityData(E_SEND_TEST_NET_PORT_CONTROL, datas, (byte) 1);
    }
    public static byte[] getSendSoftwareVersionBuf() {
        return getSendConformityData(E_SEND_SOFTWARE_VERSION_CONTROL, null, (byte) 0);
    }
    public static byte[] getSendMcuALiveBuf() {
        return getSendConformityData(E_SEND_MCU_ALIVE_CONTROL, null, (byte) 0);
    }

    public static byte[] getSendConformityData(byte control, byte[] buf, byte size) {
        byte[] bytearray;
        byte length = (byte) (size + 6);
        if (length > 64 || length < 0) {
            return null;
        }
        bytearray = new byte[length];
        bytearray[0] = E_HEAD_CODE_1;
        bytearray[1] = E_HEAD_CODE_2;
        bytearray[2] = E_HEAD_CODE_3;
        bytearray[3] = (byte) length;
        bytearray[4] = control;
        for (int i = 0; i < size; i++) {
            bytearray[5 + i] = buf[i];
        }
        bytearray[length - 1] = getCheckSum(control, buf, size);
        return bytearray;
    }

    /*bufsize 是指有效数据的大小，注意
    * */
    public static byte getCheckSum(byte control, byte[] buf, byte bufsize) {
        byte checksumbyte = 0;
        byte length = (byte) (bufsize + 6);//Head code :3  length;1 control:1 checksum:1
        if (length > 64 || length < 0) {
            return 0x00;
        }
        checksumbyte += (byte) 0xAA;
        checksumbyte += (byte) 0xBB;
        checksumbyte += (byte) 0xCC;
        checksumbyte += length;
        checksumbyte += control;
        for (int i = 0; i < bufsize; i++) {
            checksumbyte += buf[i];
        }

        return checksumbyte;
    }

}
